#include "raylib.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/wait.h>
#include <unistd.h>

// --------------------------
// Configuration
// --------------------------
#define WIN_W 800
#define WIN_H 400
#define PAD 15
#define FONT_SIZE 24
#define MAX_ITEMS 2000
#define MAX_INPUT 256
#define MAX_VISIBLE 10 // Visible items in list

// --------------------------
// Data Structures
// --------------------------
typedef struct
{
    char text[256];    // Full item text (e.g., "/usr/bin/rmenu")
    char basename[64]; // Executable name (e.g., "rmenu") – for better matching
    bool is_match;     // Does item match user input?
} Item;

// --------------------------
// Global State (Shared Between Modules)
// --------------------------
Item items[MAX_ITEMS];
int item_count = 0;
char input[MAX_INPUT] = "";
size_t cursor = 0;
int selected_idx = 0;
int scroll_offset = 0;
bool running = true;
Font app_font;

// --------------------------
// Module 1: Pipe Input Handler (Read Items from Stdin)
// --------------------------
void read_pipe_items()
{
    char buf[256];
    item_count = 0;

    // Read all items from stdin (pipe, e.g., "rmenu_path | ./rmenu")
    while (fgets(buf, sizeof(buf), stdin) && item_count < MAX_ITEMS)
    {
        // Trim trailing newlines/spaces
        size_t len = strlen(buf);
        while (len > 0 && isspace((unsigned char)buf[len - 1]))
            buf[--len] = '\0';

        // Store full text and extract basename (for matching)
        strncpy(items[item_count].text, buf, sizeof(items[item_count].text) - 1);
        const char *slash = strrchr(buf, '/');
        if (slash)
            strncpy(items[item_count].basename, slash + 1, sizeof(items[item_count].basename) - 1);
        else
            strncpy(items[item_count].basename, buf, sizeof(items[item_count].basename) - 1);

        items[item_count].is_match = true; // Show all by default
        item_count++;
    }
}

// --------------------------
// Module 1 Helper: Real-Time Filter (Updates as User Types)
// --------------------------
void filter_items()
{
    int input_len = strlen(input);
    if (input_len == 0)
    { // No input → show all items
        for (int i = 0; i < item_count; i++)
            items[i].is_match = true;
        selected_idx = 0;
        scroll_offset = 0;
        return;
    }

    // Match input against BASENAME (case-insensitive)
    int match_count = 0;
    int first_match = -1;
    for (int i = 0; i < item_count; i++)
    {
        bool match = true;
        for (int j = 0; j < input_len; j++)
        {
            if (tolower((unsigned char)input[j]) != tolower((unsigned char)items[i].basename[j]))
            {
                match = false;
                break;
            }
        }
        items[i].is_match = match;
        if (match)
        {
            match_count++;
            if (first_match == -1)
                first_match = i;
        }
    }

    // Update selection (center if possible)
    if (match_count == 0)
        selected_idx = -1;
    else
    {
        selected_idx = first_match;
        int center = MAX_VISIBLE / 2;
        scroll_offset = selected_idx - center;
        if (scroll_offset < 0)
            scroll_offset = 0;
    }
}

// --------------------------
// Module 1: Render GUI (Real-Time Updates)
// --------------------------
void render_gui()
{
    BeginDrawing();
    ClearBackground(BLACK);

    // 1. Draw Input Box
    DrawRectangle(PAD, PAD, WIN_W - 2 * PAD, FONT_SIZE + 10, DARKGRAY);

    // 2. Draw Prompt ("Filter > ")
    const char *prompt = "Filter > ";
    DrawTextEx(app_font, prompt, (Vector2){PAD + 8, PAD + 5}, FONT_SIZE, 1, WHITE);
    float prompt_w = MeasureTextEx(app_font, prompt, FONT_SIZE, 1).x;

    // 3. Draw User Input + Cursor
    DrawTextEx(app_font, input, (Vector2){PAD + 8 + prompt_w, PAD + 5}, FONT_SIZE, 1, YELLOW);
    char cursor_substr[MAX_INPUT];
    strncpy(cursor_substr, input, cursor);
    cursor_substr[cursor] = '\0';
    float cursor_x = PAD + 8 + prompt_w + MeasureTextEx(app_font, cursor_substr, FONT_SIZE, 1).x;
    DrawRectangle(cursor_x, PAD + 5, 2, FONT_SIZE, WHITE);

    // 4. Draw Separator
    int sep_y = PAD + FONT_SIZE + 20;
    DrawLine(PAD, sep_y, WIN_W - PAD, sep_y, GRAY);

    // 5. Draw Filtered Items (with scroll)
    int y = sep_y + 10;
    int visible = 0;
    for (int i = 0; i < item_count && visible < MAX_VISIBLE; i++)
    {
        if (!items[i].is_match || i < scroll_offset)
            continue;

        // Highlight selected item
        if (i == selected_idx)
            DrawRectangle(PAD, y - 2, WIN_W - 2 * PAD, FONT_SIZE + 8, BLUE);

        // Draw item text (show basename for clarity)
        DrawTextEx(app_font, items[i].basename, (Vector2){PAD + 10, y}, FONT_SIZE, 1, WHITE);

        y += FONT_SIZE + 12;
        visible++;
    }

    // 6. Draw Navigation Hint
    DrawTextEx(app_font, "↑/↓: Nav | Enter: Run | Esc: Quit", (Vector2){PAD, WIN_H - PAD - 20}, 16, 1, GRAY);

    EndDrawing();
}

// --------------------------
// Module 2: Execution (Run Selected Command)
// --------------------------
void execute_selected()
{
    const char *cmd = NULL;

    // Priority 1: Selected filtered item
    if (selected_idx != -1 && items[selected_idx].is_match)
        cmd = items[selected_idx].text;
    // Priority 2: Raw user input (if no match)
    else if (strlen(input) > 0)
        cmd = input;

    if (!cmd)
        return;

    // Fork + Exec (run command without blocking GUI)
    pid_t pid = fork();
    if (pid == -1)
        return;
    if (pid == 0)
    { // Child process
        char *args[] = {(char *)cmd, NULL};
        execvp(args[0], args);
        exit(EXIT_FAILURE); // Only reached if exec fails
    }
    else
    { // Parent process: wait for command, then exit
        int status;
        waitpid(pid, &status, 0);
        running = false;
    }
}

// --------------------------
// Module 1: Handle User Input (Real-Time)
// --------------------------
void handle_input()
{
    bool ctrl = IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_CONTROL);

    // Quit with Escape
    if (IsKeyPressed(KEY_ESCAPE))
    {
        running = false;
        return;
    }

    // Typeable characters (a-z, 0-9, symbols)
    int key = GetCharPressed();
    while (key > 0)
    {
        if (key >= 32 && key <= 126 && strlen(input) < MAX_INPUT - 1)
        {
            memmove(&input[cursor + 1], &input[cursor], MAX_INPUT - cursor - 1);
            input[cursor++] = (char)key;
            filter_items(); // Update filter in real-time
        }
        key = GetCharPressed();
    }

    // Backspace
    if (IsKeyPressed(KEY_BACKSPACE) && cursor > 0)
    {
        cursor--;
        memmove(&input[cursor], &input[cursor + 1], MAX_INPUT - cursor - 1);
        filter_items();
    }

    // Navigation (Up/Down / Ctrl+P/N)
    int center = MAX_VISIBLE / 2;
    if (IsKeyPressed(KEY_DOWN) || (ctrl && IsKeyPressed(KEY_N)))
    {
        if (selected_idx == -1)
            return;
        int next = selected_idx + 1;
        while (next < item_count && !items[next].is_match)
            next++;
        if (next < item_count)
        {
            selected_idx = next;
            scroll_offset = selected_idx - center;
            if (scroll_offset < 0)
                scroll_offset = 0;
        }
    }
    if (IsKeyPressed(KEY_UP) || (ctrl && IsKeyPressed(KEY_P)))
    {
        if (selected_idx == -1)
            return;
        int prev = selected_idx - 1;
        while (prev >= 0 && !items[prev].is_match)
            prev--;
        if (prev >= 0)
        {
            selected_idx = prev;
            scroll_offset = selected_idx - center;
            if (scroll_offset < 0)
                scroll_offset = 0;
        }
    }

    // Cursor Navigation (Left/Right / Ctrl+B/F)
    if (IsKeyPressed(KEY_RIGHT) || (ctrl && IsKeyPressed(KEY_F)))
        if (cursor < strlen(input))
            cursor++;
    if (IsKeyPressed(KEY_LEFT) || (ctrl && IsKeyPressed(KEY_B)))
        if (cursor > 0)
            cursor--;

    // Home/End (Ctrl+A/E)
    if (IsKeyPressed(KEY_HOME) || (ctrl && IsKeyPressed(KEY_A)))
        cursor = 0;
    if (IsKeyPressed(KEY_END) || (ctrl && IsKeyPressed(KEY_E)))
        cursor = strlen(input);

    // Execute with Enter
    if (IsKeyPressed(KEY_ENTER))
        execute_selected();
}

// --------------------------
// Main (Glue Between Modules)
// --------------------------
int main()
{
    // Initialize Raylib (hide debug logs)
    SetTraceLogLevel(LOG_NONE);
    InitWindow(WIN_W, WIN_H, "rmenu (Raylib)");
    SetTargetFPS(60);

    // Load system font (fallback to default if failed)
    app_font = LoadFont("/usr/share/fonts/truetype/freefont/FreeSans.ttf");
    if (app_font.baseSize == 0)
        app_font = GetFontDefault();

    // Step 1: Read items from pipe (run once before GUI)
    read_pipe_items();
    filter_items(); // Initialize filter

    // Step 2: GUI Loop (real-time input/filter/render)
    while (running && !WindowShouldClose())
    {
        handle_input(); // Module 1: Input
        render_gui();   // Module 1: Render
    }

    // Cleanup
    UnloadFont(app_font);
    CloseWindow();
    return 0;
}
